package ie.dkit.semanticPhotoWeb.server.persistence;

import ie.dkit.semanticPhotoWeb.client.ClientServerConnector;
import ie.dkit.semanticPhotoWeb.client.dtos.ImageDTO;
import ie.dkit.semanticPhotoWeb.client.dtos.InterestDTO;
import ie.dkit.semanticPhotoWeb.client.dtos.PersonDTO;
import ie.dkit.semanticPhotoWeb.server.persistence.storageObjects.Interest;
import ie.dkit.semanticPhotoWeb.server.persistence.storageObjects.Person;
import ie.dkit.semanticPhotoWeb.server.persistence.storageObjects.FOAFDAO;
import ie.dkit.semanticPhotoWeb.server.persistence.storageObjects.PictureRelation;
import ie.dkit.semanticPhotoWeb.server.persistence.storageObjects.PictureRelationDAO;

import java.util.Collection;
import java.util.logging.Logger;

import javax.sql.DataSource;

import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author Sebastian Ruehl
 * 
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place, Suite 330, Boston, MA 02111-1307 USA
 */
public class PersistenceHandler {

	private final static Logger logger = Logger.getLogger(PersistenceHandler.class
			.getName());

	private PictureRelationDAO pictureRelationDAO;

	private FOAFDAO fOAFDAO;

	private static PersistenceHandler persistenceHandler;

	protected PersistenceHandler() {
	}

	/**
	 * Creates a new instance of the persistenceHandler
	 * 
	 * @return
	 */
	public static synchronized PersistenceHandler getInstance() {
		if (persistenceHandler == null) {
			ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
					"spring.xml");
			persistenceHandler = (PersistenceHandler) context
					.getBean("PersistenceHandler");
		}
		return persistenceHandler;
	}

	/**
	 * Required for the Spring BeanLoader
	 * 
	 * @param pictureRelationDAO
	 */
	public void setPictureRelationDAO(PictureRelationDAO pictureRelationDAO) {
		this.pictureRelationDAO = pictureRelationDAO;
	}

	/**
	 * Required for the Spring BeanLoader
	 * 
	 * @param fOAFDAO
	 */
	public void setFOAFDAO(FOAFDAO fOAFDAO) {
		this.fOAFDAO = fOAFDAO;
	}

	/**
	 * Required for the Spring BeanLoader
	 * 
	 * @param dataSource
	 */
	public void setDataSource(DataSource dataSource) {
	}

	/**
	 * Add a interest to personid
	 * 
	 * @param personid
	 * @param interestDTO
	 * @throws FOAFException
	 */
	public synchronized void addInterest(int personid, InterestDTO interestDTO)
			throws FOAFException {
		synchronized (fOAFDAO) {
			Interest interest = new Interest();
			interest.setAbout(interestDTO.about);
			interest.setTitle(interestDTO.title);
			fOAFDAO.save(interest);
			Person person = getPersonByID(personid);
			person.addInterest(interest);
			fOAFDAO.save(person);
		}
	}

	/**
	 * Add a know relation to personID to person
	 * 
	 * @param personId
	 * @param person
	 * @throws FOAFException
	 */
	public synchronized void addKnowRelation(int personId, PersonDTO person)
			throws FOAFException {
		synchronized (fOAFDAO) {
			Person me = getPersonByID(personId);
			for (Person p : me.getKnows()) {
				if (p.getId() == person.personID) {
					throw new FOAFException("Person allready in List");
				}
			}
			Person him = getPersonByID(person.personID);
			if (him == null) {
				throw new FOAFException("seconde Person unknown");
			}
			me.addKnownPerson(him);
			fOAFDAO.save(me);
		}
	}

	/**
	 * Add a Person to the table
	 * 
	 * @param person
	 */
	public synchronized Person createNewUserProfile(String username) {
		synchronized (fOAFDAO) {
			Person person = new Person();
			person.setUsername(username);
			fOAFDAO.save(person);
			return person;
		}
	}

	/**
	 * Delete a picture relation
	 * 
	 * @param id
	 * @throws PictureRelationException
	 */
	public synchronized void deletePictureRelation(int id)
			throws PictureRelationException {
		synchronized (pictureRelationDAO) {
			pictureRelationDAO.delete(getPictureRelationByID(id));
		}
	}

	/**
	 * Delete a known relation
	 * 
	 * @param personId
	 * @param knownPersonID
	 * @throws FOAFException
	 */
	public synchronized void deleteKnowRelation(int personId, int knownPersonID)
			throws FOAFException {
		synchronized (fOAFDAO) {
			Person me = getPersonByID(personId);
			for (Person known : me.getKnows()) {
				if (known.getId() == knownPersonID) {
					me.removeKnownPerson(known);
					fOAFDAO.save(me);
					return;
				}
			}
			throw new FOAFException("Person not in Known List. ID:"
					+ knownPersonID);
		}
	}

	/**
	 * Delete a interest
	 * 
	 * @param personId
	 * @param interestID
	 * @throws FOAFException
	 */
	public synchronized void deleteInterest(int personId, int interestID)
			throws FOAFException {
		synchronized (fOAFDAO) {
			Person me = getPersonByID(personId);
			for (Interest interest : me.getInterests()) {
				if (interest.getInterestID() == interestID) {
					me.removeInterest(interest);
					fOAFDAO.save(me);
					fOAFDAO.delete(interest);
					return;
				}
			}
			throw new FOAFException("This interest is not in the list. ID:"
					+ interestID);
		}
	}

	/**
	 * Delete all Relations assoziated with the image
	 * 
	 * @param imageDTO
	 */
	public synchronized void deleteAllRelationsByImageDTO(ImageDTO imageDTO) {
		synchronized (pictureRelationDAO) {
			Collection<PictureRelation> relations = pictureRelationDAO
					.findAllRelationsWithContainingPictureID(imageDTO.imageID);
			for (PictureRelation relation : relations) {
				pictureRelationDAO.delete(relation);
			}
		}
	}

	/**
	 * Returns all Persons in the Database
	 * 
	 * @return
	 */
	public synchronized Collection<Person> getAllPerson() {
		synchronized (fOAFDAO) {
			return fOAFDAO.getPersons();
		}
	}

	/**
	 * @see ClientServerConnector
	 * @param dto
	 * @return
	 */
	public synchronized Collection<PictureRelation> getLinkedPictures(
			ImageDTO dto) {
		synchronized (pictureRelationDAO) {
			return pictureRelationDAO.getLinkedPictures(dto.imageID);
		}
	}

	/**
	 * Returns the Person defined by username
	 * 
	 * @param username
	 * @return
	 * @throws FOAFException
	 */
	public synchronized Person getPersonByUsername(String username)
			throws FOAFException {
		synchronized (fOAFDAO) {
			Person person = fOAFDAO.findPersonByUsername(username);
			if (person == null) {
				throw new FOAFException("Person not found");
			} else {
				return person;
			}
		}
	}

	/**
	 * Returns the Person defined by id
	 * 
	 * @param id
	 * @return
	 * @throws FOAFException
	 */
	public synchronized Person getPersonByID(int id) throws FOAFException {
		synchronized (fOAFDAO) {
			Person person = fOAFDAO.findPersonById(id);
			if (person == null) {
				throw new FOAFException("Person not found");
			} else {
				return person;
			}
		}
	}

	/**
	 * Returns all PictureRelations defined by id
	 * 
	 * @param id
	 * @return
	 * @throws PictureRelationException
	 */
	public synchronized PictureRelation getPictureRelationByID(int id)
			throws PictureRelationException {
		synchronized (pictureRelationDAO) {
			PictureRelation pictureRelation = pictureRelationDAO
					.findPictureRelationById(id);
			if (pictureRelation == null) {
				throw new PictureRelationException("PictureRelation not found");
			} else {
				return pictureRelation;
			}
		}
	}

	/**
	 * 
	 * @param dto
	 * @return
	 */
	public synchronized Collection<PictureRelation> getReferringPictures(
			ImageDTO dto) {
		synchronized (pictureRelationDAO) {
			return pictureRelationDAO.getReferringPictures(dto.imageID);
		}
	}

	/**
	 * @see ClientServerConnector
	 * @param username
	 * @param fromDTO
	 * @param toDTO
	 * @param typ
	 */
	public synchronized void linkToOtherPicture(String username,
			ImageDTO fromDTO, ImageDTO toDTO, String typ) {
		logger.info("Log this (" + fromDTO.imageID + ") picture to this ("
				+ toDTO.imageID + ") picture with this typ:" + typ);
		PictureRelation relation = new PictureRelation();
		relation.setFirstPhotoID(fromDTO.imageID);
		relation.setRelationTyp(typ);
		relation.setSecondPhotoID(toDTO.imageID);
		relation.setUsername(username);
		synchronized (pictureRelationDAO) {
			pictureRelationDAO.save(relation);
		}
	}

	/**
	 * Udate a profile defined by personID with the Data in personDTO
	 * 
	 * @param personid
	 * @param personDTO
	 * @throws FOAFException
	 */
	public synchronized void updateProfile(int personid, PersonDTO personDTO)
			throws FOAFException {
		synchronized (fOAFDAO) {
			Person person = getPersonByID(personid);
			person.setName(personDTO.name);
			person.setEmail(personDTO.email);
			person.setHomepage(personDTO.homepage);
			person.setNick(personDTO.nick);
			person.setPhoto(personDTO.photo);
			fOAFDAO.save(person);
		}
	}
}
